/**
 * @author   Ivan Andonov
 * @email:   ivan.andonov[at]gmail[dot]com
 *
 * @require  sjs, sjs.ShortcutCall
 * @use      sjs.Utils, sjs.Objects, sjs.Delegate, sjs.Debug
 * @optional 
 **/

(function (){
	
	sjs.create('sjs.Ajax', {

		// CONSTRUCTOR
		initialize : function() {
			// PRIVATE PROPS
			var requestObj = null; // current request object
			
			var queue = []; // waiting requests
			
			var loading = false;
			
			// PUBLIC METHODS
			this.get = function(link, props, onSuccess, onError, leavePrev, useCache) {
				//$log(this+'.get link='+link+' leavePrev='+leavePrev+' useCache='+useCache);
				addRequest(link, props, onSuccess, onError, 'GET', null, leavePrev, useCache);
			};
			
			this.post = function(link, props, onSuccess, onError, encoding, leavePrev, useCache) {
				//$log(this+'.get link='+link+' leavePrev='+leavePrev+' useCache='+useCache);
				addRequest(link, props, onSuccess, onError, 'POST', encoding, leavePrev, useCache);
			};
			
			this.cancel = function() {
				//$log(this+'.cancel loading='+loading+' requestObj='+requestObj);
				if (loading && requestObj != null) {
					//requestObj.request.onreadystatechange = null;
					requestObj.request.onreadystatechange = function() {};
					requestObj.request.abort();
					requestObj = null;
				}
			};
			
			// PRIVATE METHODS
			function addRequest(link, props, onSuccess, onError, method, encoding, leavePrev, useCache) {
				//$log(this+'.addRequest link='+link+' method='+method+' encoding='+encoding+' leavePrev='+leavePrev+' useCache='+useCache);
				var obj = {
					link:link,
					props:props,
					onSuccess:onSuccess,
					onError:onError,
					method:method,
					encoding:encoding,
					useCache:useCache
				};
				// (useCache ? '' : (link.indexOf('?') == -1 ? '?' : '&')+'cache='+(new Date().getTime()));
				if (loading && leavePrev) {
					queue.push(obj);
				} else {
					if (loading) this.cancel();
					loading = true;
					doRequest(obj);
				}
				return true;
			};
			
			function doRequest(obj) {
				$log(this+'.doRequest link='+obj.link+' props='+this.classInstance().propsToString(obj.props)+' method='+obj.method);
				requestObj = obj;
				requestObj.request = this.classInstance().getAjax();
				var req = requestObj.request;
				req.onreadystatechange = $delegate(this, onReadyStateChange);
				if (!obj.useCashe && obj.method != 'POST') {
					var time = new Date().getTime();
					if (obj.props.cache != null) {
						obj.props['cache'+time] = time;
					} else {
						obj.props.cache = time;
					}
				}
				var props = this.classInstance().propsToString(obj.props, true);
				switch (obj.method) {
					case 'GET':
						if (props.length) obj.link += (obj.link.indexOf('?') == -1 ? '?' : '&')+props;
						req.open(obj.method, obj.link, true);
						sjs.Utils.isIE() ? req.send() : req.send(null);
						break;
					case 'POST':
						req.open(obj.method, obj.link, true);
						//req.setRequestHeader('Content-Encoding', 'windows-1251');
						//req.setRequestHeader('If-Modified-Since', 'Thu, 01 Jan 1970 00:00:00 GMT' );
						req.setRequestHeader('Method', 'POST ' + obj.link + ' HTTP/1.1');
						req.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
						req.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset='+(obj.encoding || 'UTF-8'));
						req.send(props);
						break;
				}
			};
			
			function onReadyStateChange() {
				//$log(this+'.onReadyStateChange readyState='+requestObj.request.readyState);
				if (requestObj == null) return;
				var reqObj = requestObj;
				var req = reqObj.request;
				if (req.aborted) return;
				if (reqObj != null && req.readyState == 4) {
					delete req['onreadystatechange'];
					requestObj = null;
					loading = false;
					var haveError = false;
					try {
						if ((req.status == 200 || (util.isLocal() && req.status == 0))) {
							if (typeof(reqObj.onSuccess)=='function') {
								reqObj.onSuccess(req, reqObj);
							}
						} else if (typeof(reqObj.onError)=='function') {
							haveError = true
						}
					} catch(er) {
						haveError = true;
					}
					if (haveError && typeof(reqObj.onError)=='function') {
						reqObj.onError(req, reqObj);
					}
					reqObj.xmlHttpRequest = null;
					// call next
					if (queue.length) {
						doRequest(queue.shift());
					} else {
						loading = false;
					}
				}
			};
			
		},

		// STATIC METHODS
		/**
		* Static method which check for ajax availability
		* if ajax is not supported the onclick will return true and the href will be executed
		*
		* usage:
		* 		link.onclick = $delegate(sjs.Ajax, 'call', $delegate(target, 'method'));
		*       or
		* 		onclick="$delegate(sjs.Ajax, 'call', $delegate(target, 'method')).apply(null, arugments)"
		*/
		call : function(callback, event) {
			if (!this.available) {
				return true;
			}
			if (callback) {
				$delegate(window, callback, event)();
			}
			return false;
		},

		getAjax : function() {
			var req = false;
			if (window.XMLHttpRequest) {
				try {
					req = new XMLHttpRequest();
				} catch(e) {
					req = false;
				};
			} else if (window.ActiveXObject) {
				try {
					req = new ActiveXObject('Msxml2.XMLHTTP');
				} catch(e) {
					try {
						req = new ActiveXObject('Microsoft.XMLHTTP');
					} catch(e) {
						req = false;
					};
				};
			};
			return req;
		},

		propsToString : function(props, encode) {
			//$log('propsToString props='+props+' encode='+encode);
			var str = '';
			var tmp_str = '';
			var type;
			for (var prop in props) {
				type = typeof(props[prop]);
				switch (type.toLowerCase()) {
					case 'string':
					case 'number':
						str += (str.length ? '&' : '')+this.formatVar(prop, props[prop], encode);
						break;
					case 'object':
						tmp_str = this.formatObject((encode ? encodeURIComponent(prop) : prop), props[prop], encode, props[prop] instanceof Array);
						if (tmp_str.length) {
							str += (str.length ? '&' : '')+tmp_str;
						}
				}
			}
			return str;
		},

		formatVar : function(prop, value, encode, onlyValue) {
			//$log('formatVar prop='+prop+' value='+value+' encode='+encode+' onlyValue='+onlyValue);
			return encode ? (onlyValue ? prop : encodeURIComponent(prop))+'='+encodeURIComponent(value) : prop+'='+value;
		},

		formatObject : function(prop, props, encode, isArray) {
			//$log('formatObject prop='+prop+' props='+props+' encode='+encode+' isArray='+isArray);
			var type;
			var str = '';
			for (var prop in props) {
				type = typeof(props[prop]);
				switch (type.toLowerCase()) {
					case 'string':
					case 'number':
						str += (str.length ? '&' : '')+this.formatVar(prop+(isArray ? '[]' : '['+(encode ? encodeURIComponent(prop) : prop)+']'), props[prop], encode, true);
						break;
					case 'object':
						str += (str.length ? '&' : '')+this.formatObject(prop+(isArray ? '[]' : '['+(encode ? encodeURIComponent(prop) : prop)+']'), props[prop], encode, true, props[prop] instanceof Array);
				}
			}
			return str;
		}
	});

	// STATIC PROP
	sjs.Ajax.available = !!sjs.Ajax.getAjax();

	// create shortcuts
	if ($shortcut) {
		$shortcut(['ajax', 'ajaxCall'], sjs.Ajax, 'call');
	}
	
})();